package com.sjc.geekDesignPattern.factory.dependencyInjection;

import org.springframework.beans.factory.NoSuchBeanDefinitionException;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Classname BeansFactory
 * @Description
 * @Date 2020/5/3 18:54
 * @Created by SangJiacun
 */
public class BeansFactory {
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitions = new ConcurrentHashMap<>();

    public void addBeanDefinitions(List<BeanDefinition> beanDefinitionList) throws Exception {
        for (BeanDefinition beanDefinition : beanDefinitionList) {
            this.beanDefinitions.putIfAbsent(beanDefinition.getId(), beanDefinition);
        }

        for (BeanDefinition beanDefinition : beanDefinitionList) {
            if (beanDefinition.isLazyInit() == false && beanDefinition.isSingleton()) {
                createBean(beanDefinition);
            }
        }
    }

    public Object getBean(String beanId) {
        BeanDefinition beanDefinition = beanDefinitions.get(beanId);
        if (beanDefinition == null) {
            throw new NoSuchBeanDefinitionException("Bean is not defined: " + beanId);
        }
        try {
            return createBean(beanDefinition);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    protected Object createBean(BeanDefinition beanDefinition) throws Exception {
        if (beanDefinition.isSingleton() && singletonObjects.contains(beanDefinition.getId())){
            return singletonObjects.get(beanDefinition.getId());
        }
        Object bean = null;
            try {
                Class beanClass = Class.forName(beanDefinition.getClassName());
                List<BeanDefinition.ConstructorArg> args = beanDefinition.getConstructorArgs();
                if (args.isEmpty()) {
                    bean = beanClass.newInstance();
                } else {
                    Class[] argClasses = new Class[args.size()];
                    Object[] argObjects = new Object[args.size()];
                    for (int i = 0; i < args.size(); ++i) {
                        BeanDefinition.ConstructorArg arg = args.get(i);
                        if (!arg.isRef()) {
                            argClasses[i] = arg.getType();
                            argObjects[i] = arg.getArg();
                        } else {
                            BeanDefinition refBeanDefinition = beanDefinitions.get(arg.getArg());
                            if (refBeanDefinition == null) {
                                throw new NoSuchBeanDefinitionException("Bean is not defined: " + arg.getArg());
                            }
                            argClasses[i] = Class.forName(refBeanDefinition.getClassName());
                            argObjects[i] = createBean(refBeanDefinition);
                        }
                    }
                    bean = beanClass.getConstructor(argClasses).newInstance(argObjects);
                }
            } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e){
                throw new Exception("创建对象失败", e);
            }

            if (bean != null && beanDefinition.isSingleton()) {
                singletonObjects.putIfAbsent(beanDefinition.getId(), bean);
                return singletonObjects.get(beanDefinition.getId());
            }
            return bean;
    }
}
